package cn.edu.scau.cmi.ema.service;

import cn.edu.scau.cmi.ema.base.CmiPagination;
import cn.edu.scau.cmi.ema.domain.*;
import cn.edu.scau.cmi.ema.service.base.CmiEmaService;
import cn.edu.scau.cmi.ema.util.CmiSqlUtil;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.Query;
import java.util.*;
@Service("ProducerService")
@Transactional

public class ProducerServiceImpl extends CmiEmaService implements ProducerService {
	public ProducerServiceImpl() {
	}

	@Transactional
	public Producer saveProducer(Producer producer) {
		Set<Food> existingFoodsForProducer = new HashSet<Food>();
		Set<Purchaseadditive> existingPurchaseadditivesForProducer = new HashSet<Purchaseadditive>();
		Set<Purchasematerial> existingPurchasematerialsForProducer = new HashSet<Purchasematerial>();

		Set<Food> selectedFoodsForProducer = producer.getFoodsForProducer();
		Set<Purchaseadditive> selectedPurchaseadditivesForProducer = producer.getPurchaseadditivesForProducer();
		Set<Purchasematerial> selectedPurchasematerialsForProducer = producer.getPurchasematerialsForProducer();

		if (producer.getId() == null) {
			Address address = addressDAO.findAddressById(producer.getAddress().getId());

			Producer existingProducer = producerDAO.findProducerByPhysicalPrimaryKey(producer.getName(),
					address.getId());

			producer.setAddress(address);

			if (isEquals(producer, existingProducer)) {
				return existingProducer;
			}

			producer = producerDAO.store(producer);
			producerDAO.flush();

			for (Food relatedFood : selectedFoodsForProducer) {
				relatedFood.setProducer(producer);
				foodDAO.store(relatedFood);
			}
			for (Purchaseadditive relatedPurchaseadditive : selectedPurchaseadditivesForProducer) {
				relatedPurchaseadditive.setProducer(producer);
				purchaseadditiveDAO.store(relatedPurchaseadditive);
			}
			for (Purchasematerial relatedPurchasematerial : selectedPurchasematerialsForProducer) {
				relatedPurchasematerial.setProducer(producer);
				purchasematerialDAO.store(relatedPurchasematerial);
			}
		} else {
			Producer existingProducer = producerDAO.findProducerByPrimaryKey(producer.getId());

			existingFoodsForProducer = existingProducer.getFoodsForProducer();
			existingPurchaseadditivesForProducer = existingProducer.getPurchaseadditivesForProducer();
			existingPurchasematerialsForProducer = existingProducer.getPurchasematerialsForProducer();

			Set<Food> prepareDeleteFoodsForProducer = new HashSet<Food>();
			Set<Food> prepareAddFoodsForProducer = new HashSet<Food>();

			prepareDeleteFoodsForProducer.addAll(selectedFoodsForProducer);
			prepareDeleteFoodsForProducer.addAll(existingFoodsForProducer);

			prepareAddFoodsForProducer.addAll(selectedFoodsForProducer);
			prepareAddFoodsForProducer.addAll(existingFoodsForProducer);

			prepareDeleteFoodsForProducer.removeAll(selectedFoodsForProducer);
			prepareAddFoodsForProducer.removeAll(existingFoodsForProducer);

			for (Food relatedFood : prepareAddFoodsForProducer) {
				relatedFood.setProducer(producer);
				foodDAO.store(relatedFood);
			}

			for (Food relatedFood : prepareDeleteFoodsForProducer) {
				relatedFood.setProducer(null);
				foodDAO.store(relatedFood);
			}
			Set<Purchaseadditive> prepareDeletePurchaseadditivesForProducer = new HashSet<Purchaseadditive>();
			Set<Purchaseadditive> prepareAddPurchaseadditivesForProducer = new HashSet<Purchaseadditive>();

			prepareDeletePurchaseadditivesForProducer.addAll(selectedPurchaseadditivesForProducer);
			prepareDeletePurchaseadditivesForProducer.addAll(existingPurchaseadditivesForProducer);

			prepareAddPurchaseadditivesForProducer.addAll(selectedPurchaseadditivesForProducer);
			prepareAddPurchaseadditivesForProducer.addAll(existingPurchaseadditivesForProducer);

			prepareDeletePurchaseadditivesForProducer.removeAll(selectedPurchaseadditivesForProducer);
			prepareAddPurchaseadditivesForProducer.removeAll(existingPurchaseadditivesForProducer);

			for (Purchaseadditive relatedPurchaseadditive : prepareAddPurchaseadditivesForProducer) {
				relatedPurchaseadditive.setProducer(producer);
				purchaseadditiveDAO.store(relatedPurchaseadditive);
			}

			for (Purchaseadditive relatedPurchaseadditive : prepareDeletePurchaseadditivesForProducer) {
				relatedPurchaseadditive.setProducer(null);
				purchaseadditiveDAO.store(relatedPurchaseadditive);
			}
			Set<Purchasematerial> prepareDeletePurchasematerialsForProducer = new HashSet<Purchasematerial>();
			Set<Purchasematerial> prepareAddPurchasematerialsForProducer = new HashSet<Purchasematerial>();

			prepareDeletePurchasematerialsForProducer.addAll(selectedPurchasematerialsForProducer);
			prepareDeletePurchasematerialsForProducer.addAll(existingPurchasematerialsForProducer);

			prepareAddPurchasematerialsForProducer.addAll(selectedPurchasematerialsForProducer);
			prepareAddPurchasematerialsForProducer.addAll(existingPurchasematerialsForProducer);

			prepareDeletePurchasematerialsForProducer.removeAll(selectedPurchasematerialsForProducer);
			prepareAddPurchasematerialsForProducer.removeAll(existingPurchasematerialsForProducer);

			for (Purchasematerial relatedPurchasematerial : prepareAddPurchasematerialsForProducer) {
				relatedPurchasematerial.setProducer(producer);
				purchasematerialDAO.store(relatedPurchasematerial);
			}

			for (Purchasematerial relatedPurchasematerial : prepareDeletePurchasematerialsForProducer) {
				relatedPurchasematerial.setProducer(null);
				purchasematerialDAO.store(relatedPurchasematerial);
			}
			producer = producerDAO.store(producer);
			producerDAO.flush();
		}

		foodDAO.flush();
		purchaseadditiveDAO.flush();
		purchasematerialDAO.flush();
		return producer;
	}

	public void deleteProducer(Producer producer) {
		producerDAO.remove(producer);
		producerDAO.flush();
	}

	@Transactional
	public void deleteProducers(Set<Integer> deleteProducersId) {
		Query query = producerDAO.createNamedQuery("deleteProducersById", -1, -1);
		query.setParameter(1, deleteProducersId);
		query.executeUpdate();
	}

	@Transactional
	public Set<Producer> loadProducers() {
		return producerDAO.findAllProducers();

	}

	@Transactional
	public Set<Producer> loadReferenceProducers(Set<Producer> producers) {
		Iterator<Producer> producersIterator = producers.iterator();
		Set<Producer> referencedProducers = new HashSet<>();
		while (producersIterator.hasNext()) {
			Producer currentProducer = producersIterator.next();

			if (!currentProducer.getFoodsForProducer().isEmpty()) {
				referencedProducers.add(currentProducer);
				continue;
			}

			if (!currentProducer.getPurchaseadditivesForProducer().isEmpty()) {
				referencedProducers.add(currentProducer);
				continue;
			}

			if (!currentProducer.getPurchasematerialsForProducer().isEmpty()) {
				referencedProducers.add(currentProducer);
				continue;
			}
		}
		return referencedProducers;
	}

	public Set<Producer> loadProducersByIdSet(Set<Integer> producerIdSet) {

		Iterator<Integer> producerIdIterator = producerIdSet.iterator();
		Set<Producer> producerSet = new HashSet<>();
		while (producerIdIterator.hasNext()) {
			Integer id = producerIdIterator.next();
			Producer currentProducer = producerDAO.findProducerById(id);
			producerSet.add(currentProducer);
		}
		return producerSet;
	}

	@Transactional
	public List<Producer> findAllProducers(Integer startResult, Integer maxRows) {
		return new java.util.ArrayList<Producer>(producerDAO.findAllProducers(startResult, maxRows));
	}

	@Transactional
	public Producer findProducerByPrimaryKey(Integer id) {
		return producerDAO.findProducerByPrimaryKey(id);
	}

	@Transactional
	public Integer countProducers() {
		return ((Long) producerDAO.createQuerySingleResult("select count(o) from Producer o").getSingleResult())
				.intValue();
	}

	@Transactional
	public Integer countProducers(CmiPagination page) {
		String sql = CmiSqlUtil.createCountHql(page, Producer.class);
		return ((Long) producerDAO.createQuerySingleResult(sql).getSingleResult()).intValue();
	}

	/**
	 * search for name with ajax
	 *
	 */
	@Transactional
	public Integer countProducersByName(String queryName) {
		Query query = producerDAO.createNamedQuery("countProducersByName", -1, -1);
		query.setParameter(1, queryName);
		return new Long((long) query.getSingleResult()).intValue();
	}

	@Transactional
	public Integer countProducersByDetachedCriteria(CmiPagination page) {
		DetachedCriteria detachedCriteria = CmiSqlUtil.createDetachedCriteria(page, Producer.class);
		Session session = (Session) entityManager.getDelegate();
		detachedCriteria.setProjection(Projections.rowCount());
		return new Long((long) detachedCriteria.getExecutableCriteria(session).uniqueResult()).intValue();
	}

	public Map<String, ?> loadProducerObjectReferencedSet(Producer producer) {
		HashMap<String, Object> objectReferencedSetMap = new HashMap<>();
		if (producer.getAddress() != null) {

			Set<Integer> addressIds = new HashSet<Integer>();
			addressIds.add(producer.getAddress().getId());
			objectReferencedSetMap.put("addressIds", addressIds);
		}

		Set<Integer> foodsIds = new HashSet<Integer>();
		for (Food obj : producer.getFoodsForProducer()) {

			foodsIds.add(obj.getId());
		}
		objectReferencedSetMap.put("foodsIds", foodsIds);
		Set<Integer> purchaseadditivesIds = new HashSet<Integer>();
		for (Purchaseadditive obj : producer.getPurchaseadditivesForProducer()) {

			purchaseadditivesIds.add(obj.getId());
		}
		objectReferencedSetMap.put("purchaseadditivesIds", purchaseadditivesIds);
		Set<Integer> purchasematerialsIds = new HashSet<Integer>();
		for (Purchasematerial obj : producer.getPurchasematerialsForProducer()) {

			purchasematerialsIds.add(obj.getId());
		}
		objectReferencedSetMap.put("purchasematerialsIds", purchasematerialsIds);

		return objectReferencedSetMap;
	}

	public Map<String, ?> loadProducerClassReferencedSet(Producer producer) {
		HashMap<String, Object> classReferencedSetMap = new HashMap<>();
		classReferencedSetMap.put("addresss", addressDAO.findAllAddresss());
		classReferencedSetMap.put("foods", foodDAO.findAllFoods());
		classReferencedSetMap.put("purchaseadditives", purchaseadditiveDAO.findAllPurchaseadditives());
		classReferencedSetMap.put("purchasematerials", purchasematerialDAO.findAllPurchasematerials());

		return classReferencedSetMap;
	}

	public boolean isEquals(Producer producer, Object obj) {
		if (obj == producer)
			return true;
		if (!(obj instanceof Producer))
			return false;
		Producer comparedProducer = (Producer) obj;
		if (!CheckPropertyEqual(producer.getName(), comparedProducer.getName())) {
			return false;
		}
		if (!CheckPropertyEqual(producer.getAddress().getId(), comparedProducer.getAddress().getId())) {
			return false;
		}
		return true;
	}

	public static <T> boolean CheckPropertyEqual(T originProperty, T comparedProperty) {
		// 基本类型经过参数传递后会自动装箱
		String[] className = originProperty.getClass().getName().split("\\.");
		if (className[0].equals("java") && className[1].equals("math")) {
			return ((Comparable) originProperty).compareTo(comparedProperty) == 0 ? true : false;
		} else {
			return originProperty.equals(comparedProperty);
		}
	}
}

